﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Documents;
using System.Windows;
using System.Windows.Media;
using System.Windows.Controls.Primitives;
using System.Windows.Controls;
using System.Windows.Input;
using WPFDiagramming.Interfaces;
using WPFDiagramming.Controls;

namespace WPFDiagramming.Adorners
{
    public class ResizeAdorner : Adorner
    {
        private VisualCollection visualChildren = null;

        private Thumb topLeft, topRight, bottomLeft, bottomRight;

        public ResizeAdorner(IResizable element)
            : base((UIElement)element)
        {
            this.visualChildren = new VisualCollection(this);

            this.BuildCorners(ref topLeft, Cursors.SizeNWSE);
            this.BuildCorners(ref topRight, Cursors.SizeNESW);
            this.BuildCorners(ref bottomLeft, Cursors.SizeNESW);
            this.BuildCorners(ref bottomRight, Cursors.SizeNWSE);

            this.topLeft.DragDelta += new DragDeltaEventHandler(DragTopLeft);
            this.topRight.DragDelta += new DragDeltaEventHandler(DragTopRight);
            this.bottomLeft.DragDelta += new DragDeltaEventHandler(DragBottomLeft);
            this.bottomRight.DragDelta += new DragDeltaEventHandler(DragBottomRight);
        }

        private void DragTopLeft(object sender, DragDeltaEventArgs e)
        {
            IResizable adornedElement = this.AdornedElement as IResizable;
            Thumb hitThumb = sender as Thumb;

            if (adornedElement == null || hitThumb == null)
                return;

            DiagramPanel parentElement = adornedElement.Parent as DiagramPanel;

            foreach (UIElement selectedElement in parentElement.SelectedElements)
            {
                if (selectedElement is IResizable)
                {
                    IResizable element = selectedElement as IResizable;
                    double x = element.Right;
                    double y = element.Bottom;

                    element.Width = Math.Max(element.Width - e.HorizontalChange, hitThumb.DesiredSize.Width);
                    element.Height = Math.Max(element.Height - e.VerticalChange, hitThumb.DesiredSize.Height);

                    element.Left = x - element.Width;
                    element.Top = y - element.Height;
                }
            }
        }

        private void DragTopRight(object sender, DragDeltaEventArgs e)
        {
            IResizable adornedElement = this.AdornedElement as IResizable;
            Thumb hitThumb = sender as Thumb;

            if (adornedElement == null || hitThumb == null)
                return;

            DiagramPanel parentElement = adornedElement.Parent as DiagramPanel;

            foreach (UIElement selectedElement in parentElement.SelectedElements)
            {
                if (selectedElement is IResizable)
                {
                    IResizable element = selectedElement as IResizable;
                    double y = element.Bottom;

                    element.Width = Math.Max(element.Width + e.HorizontalChange, hitThumb.DesiredSize.Width);
                    element.Height = Math.Max(element.Height - e.VerticalChange, hitThumb.DesiredSize.Height);

                    element.Top = y - element.Height;
                }
            }
        }

        private void DragBottomLeft(object sender, DragDeltaEventArgs e)
        {
            IResizable adornedElement = this.AdornedElement as IResizable;
            Thumb hitThumb = sender as Thumb;

            if (adornedElement == null || hitThumb == null)
                return;

            DiagramPanel parentElement = adornedElement.Parent as DiagramPanel;

            foreach (UIElement selectedElement in parentElement.SelectedElements)
            {
                if (selectedElement is IResizable)
                {
                    IResizable element = selectedElement as IResizable;

                    double x = element.Right;

                    element.Width = Math.Max(element.Width - e.HorizontalChange, hitThumb.DesiredSize.Width);
                    element.Height = Math.Max(e.VerticalChange + element.Height, hitThumb.DesiredSize.Height);

                    element.Left = x - element.Width;
                }
            }
        }

        private void DragBottomRight(object sender, DragDeltaEventArgs e)
        {
            IResizable adornedElement = this.AdornedElement as IResizable;
            Thumb hitThumb = sender as Thumb;

            if (adornedElement == null || hitThumb == null)
                return;

            DiagramPanel parentElement = adornedElement.Parent as DiagramPanel;

            foreach (FrameworkElement element in parentElement.SelectedElements)
            {
                element.Width = Math.Max(element.Width + e.HorizontalChange, hitThumb.DesiredSize.Width);
                element.Height = Math.Max(e.VerticalChange + element.Height, hitThumb.DesiredSize.Height);
            }
        }

        private void BuildCorners(ref Thumb cornerThumb, Cursor cursor)
        {
            cornerThumb = new Thumb();
            cornerThumb.Height = cornerThumb.Width = 10;
            cornerThumb.Opacity = 0.40;
            cornerThumb.Background = new SolidColorBrush(Colors.Red);
            cornerThumb.Cursor = cursor;

            visualChildren.Add(cornerThumb);
        }

        protected override Size ArrangeOverride(Size finalSize)
        {
            double desiredWidth = this.AdornedElement.DesiredSize.Width;
            double desiredHeight = this.AdornedElement.DesiredSize.Height;

            double adornerWidth = this.DesiredSize.Width;
            double adornerHeight = this.DesiredSize.Height;

            topLeft.Arrange(new Rect(-adornerWidth / 2, -adornerHeight / 2, adornerWidth, adornerHeight));
            topRight.Arrange(new Rect(desiredWidth - adornerWidth / 2, -adornerHeight / 2, adornerWidth, adornerHeight)); ;
            bottomLeft.Arrange(new Rect(-adornerWidth / 2, desiredHeight - adornerHeight / 2, adornerWidth, adornerHeight)); ;
            bottomRight.Arrange(new Rect(desiredWidth - adornerWidth / 2, desiredHeight - adornerHeight / 2, adornerWidth, adornerHeight)); ;

            return finalSize;
        }

        protected override Visual GetVisualChild(int index)
        {
            return this.visualChildren[index];
        }

        protected override int VisualChildrenCount
        {
            get
            {
                return visualChildren.Count;
            }
        }
    }
}
